package cn.skyquant.quant4j.service.boot.web.controller;

import cn.skyquant.quant4j.api.dto.ResultDTO;
import cn.skyquant.quant4j.api.enums.TitansType;
import cn.skyquant.quant4j.api.enums.VType;
import cn.skyquant.quant4j.api.quant.TimeBar;
import cn.skyquant.quant4j.api.quant.TitansValue;
import cn.skyquant.quant4j.api.quant.VBar;
import cn.skyquant.quant4j.api.quant.jforex.JForexService;
import cn.skyquant.quant4j.sdk.enums.TradeDirection;
import cn.skyquant.quant4j.sdk.util.time.CalendarUtils;
import cn.skyquant.quant4j.sdk.util.time.Period;
import cn.skyquant.quant4j.service.boot.web.annotation.NoVerify;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

@RestController
@RequestMapping("/jforex")
@NoVerify
public class JForexController {

    @Autowired
    JForexService jForexService;
    private static ResultDTO err1 = ResultDTO.error("起始时间不能小于或等于终止时间");
    private static ResultDTO err2 = ResultDTO.error("选择的时间范围太大，导致K柱数目过多，请提高K柱时间维度或者缩短时间范围");
    private static ResultDTO err3 = ResultDTO.error("选择的时间范围太小，导致K柱数目太少，样本不够，请降低K柱时间维度或者拉长时间范围");

    @RequestMapping(value = "/bars",method = RequestMethod.GET)
    @ResponseBody
    public ResultDTO getbar(
            @RequestParam(value = "symbol",required = true) final String symbol,
            @RequestParam(value = "period",required = true) final Period period,
            @RequestParam(value = "td",required = true) final TradeDirection td,
            @RequestParam(value = "from",required = true) final String from,
            @RequestParam(value = "to",required = true) String to,
            @RequestParam(value = "vday",required = false) int vday,
            @RequestParam(value = "vm",required = true) int vm,
            @RequestParam(value = "titans",required = false) int titans,
            @RequestParam(value = "titansk",required = false) int titansk,
            @RequestParam(value = "titansr",required = false) double titansr,
            @RequestParam(value = "titansType",required = false) TitansType titansType) {
        if(from==null || from.trim().length()!=14 || to==null || to.trim().length()!=14){
            return ResultDTO.error1;
        }

        try {
            long begin = CalendarUtils.parseGMTStandard(from).getTime();
            long end = CalendarUtils.parseGMTStandard(to).getTime();
            long diff = end-begin;
            if(diff<=0){
                return err1;
            }
            long l = diff/1000/period.seconds;
            if(l > 400){
                return err2;
            }
            if(l < 10){
                return err3;
            }
        } catch (ParseException e) {
            return ResultDTO.error1;
        }
        if(vday%2==0){
            vday=7;
        }
        ResultDTO<List<TimeBar>> resultDTO = jForexService.getBar(symbol, period, td,from, to);
        if(!resultDTO.okData() || resultDTO.data.isEmpty()){
            return ResultDTO.error2;
        }
        List<TimeBar> timeBars = resultDTO.data;
        SimpleDateFormat f;
        if(period==Period.daily || period==Period.week ){
            f = new SimpleDateFormat("yy-M-d");
        }else if(period==Period.month ){
            f = new SimpleDateFormat("yyyy-MM");
        }else if(period==Period.h1 || period==Period.h4 ){
            f = new SimpleDateFormat("M-d HH:m");
        }else {
            f = new SimpleDateFormat("M-d H:m");
        }
        f.setTimeZone(TimeZone.getTimeZone("GMT"));
        Map<String,Object> map = new HashMap<>();
        //开始准备数据
        //1.准备label数据
        List<String> labels = new ArrayList<>();//时间轴
        List<Object[]> bars = new ArrayList<>();//K柱
        List<Object[]> vs = new ArrayList<>();//支撑位
        List<Object[]> rvs = new ArrayList<>();//压力位
        List<Object[]> waves = new ArrayList<>();//波浪
        List<Object[]> longs = new ArrayList<>();//泰坦值买入标记
        List<Object[]> shorts = new ArrayList<>();//泰坦值卖出标记
        List<VBar> vBarList = new ArrayList<>();
        for(int i=0;i<timeBars.size();i++){
            TimeBar bar = timeBars.get(i);
            String label = f.format(new Date(bar.time));
            labels.add(label);
            bars.add(new Object[]{label,bar.open,bar.close,bar.high,bar.low,bar.volumn});
            //计算V和RV
            if(vm==1){
                if(TimeBar.isV1(timeBars,i,vday)){
                    vBarList.add(new VBar(timeBars.get(i), VType.V,i));
                }
                if(TimeBar.isRV1(timeBars,i,vday)){
                    vBarList.add(new VBar(timeBars.get(i),VType.RV,i));
                }
            }else if(vm==2){
                if(TimeBar.isV2(timeBars,i,vday)){
                    vBarList.add(new VBar(timeBars.get(i), VType.V,i));
                }
                if(TimeBar.isRV2(timeBars,i,vday)){
                    vBarList.add(new VBar(timeBars.get(i),VType.RV,i));
                }
            }
            if(titans==1 && i>1){
                TitansValue tv = TimeBar.getTitans(titansType,titansk,titansr,timeBars,i);
                if(tv!=null && tv.tradeDirection==TradeDirection.LONG){
                    longs.add(new Object[]{label,bar.low,bar.low * 0.995,tv.level});
                }
                if(tv!=null && tv.tradeDirection==TradeDirection.SHORT){
                    shorts.add(new Object[]{label,bar.high,bar.high * 1.005,tv.level});
                }
            }
        }

        Set<VBar> removeVBar = new HashSet<>();

        //对支撑位和压力位的一些处理
        for(int k=1;k<vBarList.size();k++){
            VBar vBar = vBarList.get(k);
            for(int t=k-1;t>=0;t--){
                VBar vBar1 = vBarList.get(t);
                if(vBar1.vType  == vBar.vType && vBar.vType==VType.V){
                    if(vBar1.low > vBar.low){
                        break;
                    }
                    if(vBar1.high > vBar.high && vBar1.low < vBar.low){
                        removeVBar.add(vBar);
                    }
                }
                if(vBar1.vType  == vBar.vType && vBar.vType==VType.RV){
                    if(vBar1.high < vBar.high){
                        break;
                    }
                    if(vBar1.high > vBar.high && vBar1.low < vBar.low){
                        removeVBar.add(vBar);
                    }
                }
            }
        }

        Iterator<VBar> vBarIterable = vBarList.iterator();
        while (vBarIterable.hasNext()){
            VBar vBar = vBarIterable.next();
            if(removeVBar.contains(vBar)){
                vBarIterable.remove();
            }
        }

        for(VBar vBar:vBarList){
            String label = f.format(new Date(vBar.time));
            if(vBar.vType==VType.V){
                vs.add(new Object[]{label,vBar.low,vBar.low * 0.998});
            }
            if(vBar.vType==VType.RV){
                rvs.add(new Object[]{label,vBar.high,vBar.high * 1.002});
            }
        }


        if(vm>0){
            TimeBar firstbar = timeBars.get(0);
            Set<String> varBarlabelSet = new HashSet<>();
            String firstlabel = f.format(new Date(firstbar.time));
            waves.add(new Object[]{firstlabel,firstbar.open});
            varBarlabelSet.add(firstlabel);
            VBar last = null;
            for(int i=0;i<vBarList.size();i++){
                VBar vBar = vBarList.get(i);
                String vBarLabel = f.format(new Date(vBar.time));
                if(varBarlabelSet.contains(vBarLabel)){
                    continue;
                }
                if(last==null){
                    if(vBar.vType == VType.V && vBar.low <= TimeBar.getMinLow(0,vBar.i,timeBars)){
                        last = vBar;
                    }else if(vBar.vType == VType.RV && vBar.high >= TimeBar.getMaxHigh(0,vBar.i,timeBars)){
                        last = vBar;
                    }
                }else{
                    String lastvarlabel = f.format(new Date(last.time));
                    if(last.vType == VType.V){
                        //如果last=V，那么b要找RV，如果还是找到V，并且low更低，那么就继续将low赋给last
                        if(vBar.vType==VType.V && vBar.low <= TimeBar.getMinLow(last.i,vBar.i,timeBars) ){
                            last = vBar;
                        }else if(vBar.vType == VType.RV){
                            varBarlabelSet.add(lastvarlabel);
                            waves.add(new Object[]{lastvarlabel,last.low});
                            last = vBar;
                        }
                    }else if(last.vType == VType.RV){
                        //如果last=RV，那么b要找V，如果还是找到RV，并且high更高，那么就继续将high赋给last
                        if(vBar.vType==VType.RV && vBar.high >= TimeBar.getMaxHigh(last.i,vBar.i,timeBars) ){
                            last = vBar;
                        }else if(vBar.vType == VType.V){
                            varBarlabelSet.add(lastvarlabel);
                            waves.add(new Object[]{lastvarlabel,last.high});
                            last = vBar;
                        }
                    }
                }
            }
            TimeBar lastbar = timeBars.get(timeBars.size()-1);
            String lastlabel = f.format(new Date(lastbar.time));
            if(!varBarlabelSet.contains(lastlabel)){
                varBarlabelSet.add(lastlabel);
                waves.add(new Object[]{lastlabel,lastbar.close});
            }
        }

        map.put("labels",labels);
        map.put("bars",bars);
        if(vm>0){
            map.put("vs",vs);
            map.put("rvs",rvs);
            map.put("waves",waves);
        }
        if(titans>0){
            map.put("longs",longs);
            map.put("shorts",shorts);
        }
        return ResultDTO.success(map);
    }

    @RequestMapping(value = "/time",method = RequestMethod.GET)
    @ResponseBody
    public ResultDTO getTime() {
        return jForexService.getTime();
    }

    @RequestMapping(value = "/tick/{symbol}",method = RequestMethod.GET)
    @ResponseBody
    public ResultDTO getLastTick(
            @PathVariable(value = "symbol",required = true) String symbol) {
        return jForexService.getLastTick(symbol);
    }

    @RequestMapping(value = "/tick",method = RequestMethod.GET)
    @ResponseBody
    public ResultDTO getAllTick() {
        return jForexService.getAllTick();
    }

}
